#include "LightingCommon.fxh"
#include "ShadowFactor.fxh"

cbuffer cbPerFrame
{
	float4x4 gProjInv;
};

cbuffer cbPerLight
{
	float3 gLightDirV;
	float4 gLightColorAndIntens;
#ifdef NO_SHADOWS
#else
	// Since in PS pixel's view space position is obtained, in order to draw shadows
	// it is necessary to transform it to light projection space, in following order:
	// view space -> world space -> light space -> light projection space
	// Entire transformations is in this matrix
	float4x4 gInvView_LightViewProjs[NUM_CASCADES];
	float4 gCascadeDepths;
#endif
};

#ifdef NO_SHADOWS
#else
Texture2D gBigShadowMap;
#endif

struct VS_IN
{
	float3 posL     : POSITION;
};

struct VS_OUT
{
	float4 posD         : SV_POSITION;
	float3 viewRayV     : TEXCOORD1;
};

struct PS_IN
{
	float4 posD         : SV_POSITION;
	float3 viewRayV     : TEXCOORD1;
#ifdef MSAA
	uint sampleIndex : SV_SAMPLEINDEX;
#endif
};

VS_OUT VS(VS_IN vIn)
{
	VS_OUT vOut;

	float4 posH = float4(vIn.posL, 1.0f);
	vOut.posD = posH;

	// multiplication gives view space vertex position, which is the same thing as view ray in view space
	vOut.viewRayV = mul(posH, gProjInv).xyz;
	
	return vOut;
}

#ifdef NO_SHADOWS
#else

//float calculateShadowFactorBig(int cascadeIdx, float3 viewSpacePos)
//{
//	const float2 texCoordBase[4] = { float2(0, 0), float2(0.5, 0), float2(0, 0.5), float2(0.5, 0.5) };
//
//	float4 lightSpacePos = mul(float4(viewSpacePos, 1.0f), gInvView_LightViewProjs[cascadeIdx]);
//	lightSpacePos = lightSpacePos / lightSpacePos.w;
//
//	const float depth = lightSpacePos.z;
//	const float2 smTexCoord = texCoordBase[cascadeIdx] + (lightSpacePos.xy * float2(0.5f, -0.5f) + float2(0.5f, 0.5f)) * 0.5f;
//
//	//const float smTexelSize = 0.5f / 1024.0f;
//	const float smTexelSize = 0.5f / CASCADE_MAP_SIZE;
//
//	float shadowFactor = 0.0f;
//	const int pcfKernelHalfSize = 1;
//	const float pcfKernelNumSamplesInv = 1.0f / ( (2 * pcfKernelHalfSize + 1) * (2 * pcfKernelHalfSize + 1) );
//	for(float x = -pcfKernelHalfSize; x <= pcfKernelHalfSize; x += 1.f)
//	{
//		for(float y = -pcfKernelHalfSize; y <= pcfKernelHalfSize; y += 1.f)
//		{
//			shadowFactor += gBigShadowMap.SampleCmpLevelZero(ShadowSampler, smTexCoord + float2(x, y) * smTexelSize, depth);
//		}
//	}
//	shadowFactor *= pcfKernelNumSamplesInv;
//	
//	//float shadowFactor = shadowMap.SampleCmpLevelZero(ShadowSampler, smTexCoord, depth);
//
//	return shadowFactor;
//}

#endif

float4 PS(PS_IN pIn) : SV_Target
{
	float3 viewRayV = normalize(pIn.viewRayV);
	int3 texCoord = int3(pIn.posD.xy, 0);

	float3 normalV, diffColor;
	float specIntens, specPower;

#ifdef MSAA
	float3 posV = viewRayV * getDepthFromGBufferLoadMS(pIn.sampleIndex, texCoord);
	getPixelParamsFromGBufferLoadMS(pIn.sampleIndex, texCoord, normalV, diffColor, specIntens, specPower);
#else
	float3 posV = viewRayV * getDepthFromGBufferLoad(texCoord);
	getPixelParamsFromGBufferLoad(texCoord, normalV, diffColor, specIntens, specPower);
#endif

	float3 lighting = calculateLighting(viewRayV, -gLightDirV, normalV, gLightColorAndIntens, diffColor, specIntens, specPower);

	float shadowFactor = 1.0f;
	float3 cascadeColor = float3(1, 1, 1);

#ifdef NO_SHADOWS
#else
	//float4 depthCmp = posV.z < gCascadeDepths;
	//float cascadeIdx = dot(depthCmp, float4(-1, -1, -1, 3));

	//int cascadeIdx = 0;
	//cascadeIdx = posV.z > gCascadeDepths.x ? 1 : cascadeIdx;
	//cascadeIdx = posV.z > gCascadeDepths.y ? 2 : cascadeIdx;
	//cascadeIdx = posV.z > gCascadeDepths.z ? 3 : cascadeIdx;

	//shadowFactor = calculateShadowFactorBig(cascadeIdx, posV);

	if(posV.z > gCascadeDepths.z)
	{
		float4 lightSpacePos = mul(float4(posV, 1.0f), gInvView_LightViewProjs[3]);
		shadowFactor = calculateShadowFactorCSM(gBigShadowMap, float2(0.5, 0.5), lightSpacePos);

		float blendLength = (gCascadeDepths.w - gCascadeDepths.z) * 0.05f;
		float blendStart = gCascadeDepths.z + blendLength;
		if(posV.z < blendStart)
		{
			cascadeColor.rb = 0.6f;
			float4 lightSpacePosBlend = mul(float4(posV, 1.0f), gInvView_LightViewProjs[2]);
			float shadowFactorBlend = calculateShadowFactorCSM(gBigShadowMap, float2(0.0, 0.5), lightSpacePosBlend);
			float t = (blendStart - posV.z) / blendLength;
			shadowFactor = t * shadowFactorBlend + (1.0f - t) * shadowFactor;
		}
	}
	else if(posV.z > gCascadeDepths.y)
	{
		float4 lightSpacePos = mul(float4(posV, 1.0f), gInvView_LightViewProjs[2]);
		shadowFactor = calculateShadowFactorCSM(gBigShadowMap, float2(0.0, 0.5), lightSpacePos);
		//cascadeColor.rg = 0.9f;

		float blendLength = (gCascadeDepths.z - gCascadeDepths.y) * 0.1f;
		float blendStart = gCascadeDepths.y + blendLength;
		if(posV.z < blendStart)
		{
			cascadeColor.rg = 0.6f;
			float4 lightSpacePosBlend = mul(float4(posV, 1.0f), gInvView_LightViewProjs[1]);
			float shadowFactorBlend = calculateShadowFactorCSM(gBigShadowMap, float2(0.5, 0.0), lightSpacePosBlend);
			float t = (blendStart - posV.z) / blendLength;
			shadowFactor = t * shadowFactorBlend + (1.0f - t) * shadowFactor;
		}
	}
	else if(posV.z > gCascadeDepths.x)
	{
		float4 lightSpacePos = mul(float4(posV, 1.0f), gInvView_LightViewProjs[1]);
		shadowFactor = calculateShadowFactorCSM(gBigShadowMap, float2(0.5, 0.0), lightSpacePos);
		//cascadeColor.rb = 0.9f;

		float blendLength = (gCascadeDepths.y - gCascadeDepths.x) * 0.15f;
		float blendStart = gCascadeDepths.x + blendLength;
		if(posV.z < blendStart)
		{
			cascadeColor.gb = 0.6f;
			float4 lightSpacePosBlend = mul(float4(posV, 1.0f), gInvView_LightViewProjs[0]);
			float shadowFactorBlend = calculateShadowFactorCSM(gBigShadowMap, float2(0.0, 0.0), lightSpacePosBlend);
			float t = (blendStart - posV.z) / blendLength;
			shadowFactor = t * shadowFactorBlend + (1.0f - t) * shadowFactor;
		}
	}
	else
	{
		float4 lightSpacePos = mul(float4(posV, 1.0f), gInvView_LightViewProjs[0]);
		shadowFactor = calculateShadowFactorCSM(gBigShadowMap, float2(0.0, 0.0), lightSpacePos);
		//cascadeColor.gb = 0.9f;
	}
	
	//clip(shadowFactor - 0.01f);
#endif

	return float4((1.0f - shadowFactor) * lighting * 0.5f + lighting * shadowFactor, 1.0f);
}

technique11 tech
{
	pass P0
	{
		SetVertexShader( CompileShader( vs_5_0, VS() ) );
		SetGeometryShader( NULL );
		SetPixelShader( CompileShader( ps_5_0, PS() ) );
		
		SetDepthStencilState(DepthOff, 0);

#ifdef MSAA
		SetRasterizerState(CullBackMS);
#else
		SetRasterizerState(CullBack);
#endif
	}
}